package com.jiliason.segmentation.doMain.event;

import com.google.common.collect.Lists;
import com.jiliason.segmentation.constant.Ct;
import com.jiliason.segmentation.constant.enums.Addr;
import com.jiliason.segmentation.constant.enums.Regex;
import com.jiliason.segmentation.constant.enums.Strategy;
import com.jiliason.segmentation.constant.enums.TextureTag;
import com.jiliason.segmentation.entity.datastructures.PolyTree;
import com.jiliason.segmentation.entity.address.AddressUnitPojo;
import com.jiliason.segmentation.entity.splitterStrategy.StrategyInPojo;
import com.jiliason.segmentation.utils.StrUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;


@Component
public class AddrParsingSegment {

    protected final String Tag = Ct.AddrParsing;

    @Resource
    private AddrTextureEngine addrTextureEngine;

    /**
    * @description _解析原则:一name，一type。
    * @param in 被解析对象
    * @return null
    * @date 2023/12/27
    * @author sjy
    */
    public List<AddressUnitPojo> divide(StrategyInPojo in, List<String> dictSegRes) {
        // 扫描定义的全段，
        List<String> strFragment = creatFragmentSet(in, dictSegRes);
        // 根据碎片段list，生成name-type的的多叉树集合
        Deque<PolyTree<String>> scanRes = scanningAllType(strFragment);
        // 解析
        return parsing(in, scanRes);
    }

    private List<AddressUnitPojo> parsing(StrategyInPojo in, Deque<PolyTree<String>> scanRes) {

        // 初始化解析结果
        List<AddressUnitPojo> parsingRes = Lists.newArrayList();
        if (scanRes.isEmpty()) return parsingRes;

        while (!scanRes.isEmpty()) {
            PolyTree<String> ntTree = scanRes.removeFirst();
            List<PolyTree<String>> childType = ntTree.getChildren();
            if (childType.isEmpty()) continue;

            childTypeParse(in, ntTree, childType, parsingRes);
        }

        if (parsingRes.isEmpty()) return parsingRes;

        return mergeDuplicates(parsingRes);

    }

    /**
    * @description _合并结果是重复的对象
    * @param parsingRes 解析结果
    * @return List<AddressUnitPojo> 返回没有重复的结果
    * @date 2024/1/12
    * @author sjy
    */
    private List<AddressUnitPojo> mergeDuplicates(List<AddressUnitPojo> parsingRes) {
        Map<Addr, List<AddressUnitPojo>> groupedByFiledName = parsingRes.stream().collect(Collectors.groupingBy(AddressUnitPojo::getFiledName));
        Set<Map.Entry<Addr, List<AddressUnitPojo>>> entries = groupedByFiledName.entrySet();
        List<AddressUnitPojo> res = Lists.newArrayList();
        for (Map.Entry<Addr, List<AddressUnitPojo>> en : entries) {
            AddressUnitPojo unit;
            if (en.getValue().size() == 1) unit = en.getValue().get(0);
            else unit = mergeUint(en.getValue());

            if (unit.getSubUnit() != null) {
                res.add(unit.getSubUnit());
                unit.setSubUnit(null);
            }
            res.add(unit);
        }
        return res;
    }

    private AddressUnitPojo mergeUint(List<AddressUnitPojo> value) {
        // 主段merge
        AddressUnitPojo unit = new AddressUnitPojo(value.get(0).getFiledName(), Tag, value.get(0).getFiledName().i());
        unit.setTypeFiledName(value.get(0).getTypeFiledName());

        StringJoiner nameJoiner = new StringJoiner("/");
        StringJoiner typeJoiner = new StringJoiner("/");
        StringJoiner ntJoiner = new StringJoiner("/");


        for (AddressUnitPojo u : value) {
            if (StringUtils.isNotEmpty(u.getName())) nameJoiner.add(u.getName());
            if (StringUtils.isNotEmpty(u.getType())) typeJoiner.add(u.getType());
            if (StringUtils.isNotEmpty(u.getFiledValue())) ntJoiner.add(u.getFiledValue());
        }

        unit.setFiledValue(ntJoiner.toString());
        unit.setName(nameJoiner.toString());
        unit.setType(typeJoiner.toString());
        unit.setSegStatus(Strategy.M.c());

        // 辅助段merge
        mergeSubUint(value, unit);

        return unit;
    }

    private void mergeSubUint(List<AddressUnitPojo> value, AddressUnitPojo unit) {
        List<String> subFiled = Lists.newArrayList();
        Addr sub = null;
        for (AddressUnitPojo u : value) {
            if (StringUtils.isNotEmpty(u.getFiledValue())) {
                subFiled.add(u.getSubUnit() == null ? "" : u.getSubUnit().getFiledValue());
                sub = u.getSubUnit() == null ? sub : u.getSubUnit().getFiledName();
            }
        }

        if (Objects.nonNull(sub)) {
            AddressUnitPojo subUnit = new AddressUnitPojo(sub, Tag, sub.i());
            subUnit.setFiledValue(String.join("/", subFiled));
            subUnit.setSegStatus(Strategy.M.c());
            unit.setSubUnit(subUnit);
        }

    }

    private void childTypeParse(StrategyInPojo in, PolyTree<String> name, List<PolyTree<String>> childType, List<AddressUnitPojo> parsingRes) {
        StringBuilder mainType = new StringBuilder();
        Addr typeTag = null;
        for (PolyTree<String> type : childType) {
            if (!type.getChildren().isEmpty()) continue;
            mainType.append(type.getData());
            typeTag = chkFiledType(typeTag, type.getData());
        }
        if (typeTag == null) {
            if (mainType.isEmpty()
                    && childType.stream().noneMatch(st -> st.getChildren().isEmpty())
                    && name.getData().length() > 1) {
                typeTag = Addr.POI_STR;
            } else {
                return;
            }
        }
        AddressUnitPojo mainUint = creatFiled(typeTag, name.getData(), mainType.toString(), in);
        subChildTypeParse(typeTag, childType, in, mainUint);
        parsingRes.add(mainUint);
    }

    private void subChildTypeParse(Addr typeTag, List<PolyTree<String>> childType, StrategyInPojo in, AddressUnitPojo mainUint) {

        StringBuilder detail = new StringBuilder();
        List<PolyTree<String>> subChildType = childType.stream().filter(t -> !t.getChildren().isEmpty()).toList();
        if (subChildType.isEmpty()) return;

        for (PolyTree<String> subType : subChildType) {
            String name = subType.getData();
            StringBuilder subTypeStr = new StringBuilder();
            List<PolyTree<String>> children = subType.getChildren();
            for (PolyTree<String> c : children) {
                subTypeStr.append(c.getData());
            }
            detail.append(name).append(subTypeStr);
        }
        AddressUnitPojo subUnit = creatDescFiled(typeTag, detail.toString(), in);
        mainUint.setSubUnit(subUnit);
    }

    private AddressUnitPojo creatFiled(Addr typeTag, String name, String nType, StrategyInPojo in) {
        AddressUnitPojo unit = new AddressUnitPojo(typeTag, Tag, typeTag.i());
        unit.setTypeFiledName(typeTag.equals(Addr.ROAD) ? Addr.ROAD_TYPE : Addr.POI_STR_TYPE);

        unit.setFiledValue(name + nType);
        unit.setName(name);
        unit.setType(nType);
        unit.setSegStatus(Strategy.M.c());

        in.split(name + nType);

        return unit;
    }

    private AddressUnitPojo creatDescFiled(Addr typeTag, String detail, StrategyInPojo in) {
        if (!Addr.ROAD.equals(typeTag) && !Addr.POI_STR.equals(typeTag)) return null;
        Addr descFiled = typeTag.equals(Addr.ROAD) ? Addr.ROAD_DETAIL : Addr.POI_DETAIL;
        AddressUnitPojo unit = new AddressUnitPojo(descFiled, Tag, descFiled.i());
        unit.setFiledValue(detail);
        unit.setSegStatus(Strategy.M.c());
        in.split(detail);
        return unit;
    }

    private Addr chkFiledType(Addr oldTag, String data) {
        Addr newTag = null;
        if (data.matches(Regex.ar5.v())) {
            newTag = Addr.ROAD;
        } else if (data.matches(Regex.ar6.v())) {
            newTag = Addr.POI_STR;
        }
        return newTag == null ? oldTag : newTag;
    }

    public Deque<PolyTree<String>> scanningAllType(List<String> strFragment) {
        int cur = 0;
        int len = strFragment.size();
        Deque<PolyTree<String>> res = new ArrayDeque<>();

        while(cur < len) {
            String fragment = strFragment.get(cur);
            if (!fragment.matches(Regex.arPt.v())) {
                cur = creatNT_Tree(cur + 1, len, strFragment, res);
            } else {
                res.addLast(new PolyTree<>(fragment));
                cur++;
            }
        }

        return res;

    }

    /**
    * @description _生成合理的碎片list
    * @param in,dictSegRes 分割对象，dict分词的结果
    * @return 碎片list
    * @date 2024/1/17
    * @author sjy
    */
    private List<String> creatFragmentSet(StrategyInPojo in, List<String> dictSegRes) {
        // 粉碎串
        List<String> strFragment = strRipper(in, dictSegRes);

        // 生成碎片list的映射纹理list
        String[] tags = addrTextureEngine.execute(strFragment);

        // 合并合理处理后的name
        return nameCombine(tags, strFragment);
    }

    private List<String> nameCombine(String[] tags, List<String> strFragment) {
        List<String> res = Lists.newArrayList();

        int len = strFragment.size();

        StringBuilder na = new StringBuilder();
        for (int i = 0; i < len; i++) {
            String t = tags[i];
            String frg = strFragment.get(i);
            if (t.startsWith(TextureTag.Name.v()) || t.equals(TextureTag.Xiu.v())) {
                na.append(frg);
            } else {
                if (!na.isEmpty()) {
                    res.add(na.toString());
                    na = new StringBuilder();
                }
                res.add(frg);
            }
        }
        if (!na.isEmpty()) {
            res.add(na.toString());
        }

        return res;
    }

    private int creatNT_Tree(int cur, int len, List<String> strFragment, Deque<PolyTree<String>> res) {
        String temp = strFragment.get(cur - 1);
        PolyTree<String> nameRoot = new PolyTree<>(temp);
        if (temp.matches(Regex.arDescription.v())) {
            res.addLast(nameRoot);
            return cur;
        }

        Deque<String> deque = new ArrayDeque<>();
        String inTypeRegex = StrUtil.combineRegex(Regex.arPt.v(), Regex.cr1.v(), Regex.cr2.v(),
                Regex.cr3.v());
        String isType;
        while (cur < len) {
            isType = strFragment.get(cur);
            if (!isType.matches(inTypeRegex)) break;
            deque.addLast(isType);
            cur++;
        }

        if (deque.isEmpty()) {
            res.addLast(nameRoot);
            // if (Objects.nonNull(isType)) res.addLast(new PolyTree<>(isType));
            return cur;
        }

        creatNT_Tree_Child(deque, nameRoot);
        res.addLast(nameRoot);
        return cur;
    }

    private void creatNT_Tree_Child(Deque<String> deque, PolyTree<String> nameRoot) {
        while (!deque.isEmpty()) {
            String childStr = deque.removeFirst();
            PolyTree<String> child = new PolyTree<>(childStr);
            if (!childStr.matches(Regex.arPt.v())) {
                // 连续数字合并
                combineNoName(deque, child);
                // 数字字母型child装载
                loadNoChild(deque, child);
            }
            nameRoot.addChild(child);
        }
    }

    private void loadNoChild(Deque<String> deque, PolyTree<String> child) {
        PolyTree<String> childSub = new PolyTree<>("");
        child.addChild(childSub);
        while (!deque.isEmpty()) {
            if (!deque.peekFirst().matches(Regex.arPt.v())) break;
            String childSubStr = deque.removeFirst();
            childSub = new PolyTree<>(childSubStr);
            child.addChild(childSub);
        }

    }

    private void combineNoName(Deque<String> deque, PolyTree<String> child) {
        while (!deque.isEmpty()) {
            if (!deque.peekFirst().matches(StrUtil.combineRegex(Regex.cr1.v(), Regex.cr2.v(), Regex.cr3.v()))) break;
            String noSubName = deque.removeFirst();
            child.setData(child.getData() + noSubName);
        }
    }

    public List<String> strRipper(StrategyInPojo in, List<String> dictSegRes) {

        String rippingTarget = StrUtil.combineRegex(
                // 全部type
                Regex.arPt.v(),
                // 数字
                Regex.cr1.v(),
                // 大小写字母
                Regex.cr2.v(),
                // 中文数字
                Regex.cr3.v(),
                // 描述词
                Regex.arDescription.v(),
                // 词库分词的结果
                String.join("|", dictSegRes));

        return StrUtil.splittingStr(in.getAddrStr(), rippingTarget);
    }



}
